package com.xxl.job.executor.service;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.bizsys.domain.CreditAccount;
import com.ruoyi.bizsys.domain.CustBankCard;
import com.ruoyi.bizsys.domain.LoanIou;
import com.ruoyi.bizsys.domain.RepaymentOrder;
import com.ruoyi.bizsys.service.ICustBankCardService;
import com.ruoyi.bizsys.service.ICustomerInfoService;
import com.ruoyi.bizsys.service.ILoanIouService;
import com.ruoyi.bizsys.service.IRepaymentOrderService;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.business.component.RepaymentMqSendService;
import com.ruoyi.common.constant.LoanConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.extdata.common.config.PayStackConfig;
import com.ruoyi.extdata.service.IExternalApiService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Future;

/**
 * @author wqg
 * @description
 * @date 2022/8/26
 */
@Service
@Slf4j
public class AsyncBizService {

    @Autowired
    RedisCache redisCache;

    @Autowired
    ICustomerInfoService customerInfoService;
    @Autowired
    ILoanIouService loanIouService;
    @Autowired
    ICustBankCardService custBankCardService;
    @Autowired
    IExternalApiService externalApiService;
    @Autowired
    PayStackConfig payStackConfig;
    @Autowired
    IRepaymentOrderService repaymentOrderService;
    @Autowired
    RepaymentMqSendService repaymentMqService;

    /**
     * 异步执行余额代扣，要重新查询iou的数据，加锁进行操作
     *
     * @param account
     * @return
     */
    @Async("threadPoolTaskExecutor")
    public Future<String> dealIouCardWithhold(CreditAccount account) {
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + account.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();
            //执行账户跑批
            doAccountCardWithhold(account.getCustNo());
            //无需重试
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
        } finally {
            lock.unlock();
        }
        return new AsyncResult(account.getCustNo());
    }

    private void doAccountCardWithhold(String custNo) {
        // select * from loan_iou where iou_status != 1 and IF(is_deferred = 1,DATEDIFF(SYSDATE(), deferred_repay_date)>=0,DATEDIFF(SYSDATE(), repay_date)>=0)
        List<LoanIou> loanIouList = loanIouService.getCardWithholdIouByCustNo(custNo);
        if (CollectionUtils.isEmpty(loanIouList)) {
            log.info("余额代扣：custNo：{},数据为空", custNo);
            return;
        }
        log.info("余额代扣：custNo：{},size:{}", custNo, loanIouList.size());
        for (int i = 0; i < loanIouList.size(); i++) {
            dealOneIouCardWithhold(loanIouList.get(i));
        }
    }

    private void dealOneIouCardWithhold(LoanIou loanIou) {
        log.info("余额代扣：custNo：{},LoanSerialNo:{}", loanIou.getCustNo(), loanIou.getLoanSerialNo());
        //查找用户的银行卡集合
        CustBankCard bankCardquery = new CustBankCard();
        bankCardquery.setCustNo(loanIou.getCustNo());
        bankCardquery.setState(UserConstants.BUSINESS_NORMAL);
        List<CustBankCard> list = custBankCardService.selectCustBankCardList(bankCardquery);
        if (CollectionUtils.isEmpty(list)) {
            log.info("余额代扣：custNo：{},卡为空", loanIou.getCustNo());
            return;
        }
        for (int i = 0; i < list.size(); i++) {
            String reference = IdUtils.reqId() + BizConstants.PAY_TRANS_FLAG_CARD_WITHHOLD;
            BigDecimal needAmt = LoanIou.calculationOverdueAmount(loanIou);
            JSONObject params = new JSONObject();
            params.put("authorization_code", list.get(i).getAuthorizationCode());
            params.put("currency", "NGN");
            params.put("amount", needAmt.setScale(0, BigDecimal.ROUND_UP).multiply(new BigDecimal(100)));
            params.put("email", list.get(i).getEmail());
            params.put("reference", reference);
            try {
                boolean isOk = isPartialDebitsEd(params, loanIou, reference);
                if (isOk) {
                    break;
                }
            } catch (Exception e) {
                log.error("代扣异常,换一张", e.getMessage());
                // 请求的api 返回false ，是抛出异常，所以需要catch 不再往外抛出
                continue;
            }
        }
    }

    private boolean isPartialDebitsEd(JSONObject params, LoanIou loanIou, String paymentReference) {
        // 创建还款订单，标记2，发起http 标记5，等待结果，冲账或者失败标记0，remark 余额代扣失败
        log.info("余额代扣：params:{}", params.toJSONString());
        RepaymentOrder repaymentOrder = repaymentOrderService.createRepaymentOrderToWait(loanIou
                , BizConstants.LOAN_REPAYMENT_ORDER_TYPE_PARTIAL_DEBITS_PAYSTACK, paymentReference);
        if (null == repaymentOrder) {
            log.info("余额代扣：创建订单失败，{}", loanIou.getLoanSerialNo());
            return true;
        }
        JSONObject apiRes = null;
        try {
            apiRes = externalApiService.dealPaystackForCardWithhold("Paystack-Partial-Debits", payStackConfig.getPartialDebit(), "POST", params);
            log.info("Paystack-Partial-Debits apiRes:{}", apiRes.toJSONString());
            if (apiRes.getBoolean("status")) {
                dealApiResSuccess(repaymentOrder, apiRes.getJSONObject("data"));
                return true;
            } else {
                // 失败标记订单失败
                updateRepaymentOrderFaild(repaymentOrder, apiRes.getString("message"));
            }
        } catch (BusinessException e) {
            updateRepaymentOrderFaild(repaymentOrder, e.getDetails());
            log.info("Paystack-Partial-Debits:余额代扣失败");
            throw new BusinessException("Paystack-Partial-Debits:余额代扣失败");
        }
        return false;
    }

    private void dealApiResSuccess(RepaymentOrder repaymentOrder, JSONObject data) {
        log.info("Paystack-Partial-Debits:余额代扣status:{}", data.getString("status"));
        // 成功标记订单为5，进行冲账 发送mq
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
        repaymentOrder.setRemark("apiRes返回true，等待mq消费");
        repaymentOrder.setRepaymentAmt(data.getBigDecimal("amount").divide(new BigDecimal("100")));
        repaymentOrder.setPayTime(data.getDate("transaction_date"));
        repaymentOrderService.updateRepaymentOrder(repaymentOrder);
        if ("success".equals(data.getString("status"))) {
            // 发送mq
            repaymentMqService.cardBatchDebitRepayment(repaymentOrder, data);
        }
    }

    private void updateRepaymentOrderFaild(RepaymentOrder repaymentOrder, String msg) {
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
        repaymentOrder.setRemark(msg);
        repaymentOrderService.updateRepaymentOrder(repaymentOrder);
    }

}
